package com.yixuan.mt.common.pool;

import com.yixuan.mt.common.entity.Objects;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.LinkedList;
import java.util.logging.Logger;

@Slf4j
public class FlyDataSource implements DataSource, EventsDestination {

    private final LinkedList<FlyConnection> connections = new LinkedList<>();
    private final String url;
    private final String username;
    private final String password;
    public String eventsSQL;
    private boolean isAlive = false;

    public FlyDataSource(int maxConnections, String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
        while (connections.size() < maxConnections) {
            if (!addConnection()) break;
        }
    }

    private boolean addConnection() {
        try {
            Connection connection = DriverManager.getConnection(url, username, password);
            synchronized (connections) {
                connections.add(new FlyConnection(connection, connections));
            }
            return true;
        } catch (SQLException e) {
            log.warn("{}:{}", url, e.getMessage());
            return false;
        }
    }

    @Override
    public FlyConnection getConnection() throws SQLException {
        synchronized (connections) {
            while (connections.isEmpty()) {
                try {
                    connections.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return connections.removeFirst();
        }
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    public void setEventsSQL(String eventsSQL) {
        this.eventsSQL = eventsSQL;
    }

    @Override
    public boolean test() {
        if (connections.isEmpty() && !addConnection()) {
            this.isAlive = false;
            return false;
        }
        FlyConnection connection = null;
        try {
            connection = getConnection();
            connection.createStatement().execute("select 1");
            connection.close();
            this.isAlive = true;
            return true;
        } catch (SQLException e) {
            try {
                if (connection != null) {
                    connection.trulyClose();
                }
            } catch (SQLException ignored) {
            }
            this.isAlive = false;
            return false;
        }
    }

    @Override
    public boolean send(Objects objects) {
        FlyConnection connection;
        try {
            connection = getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(eventsSQL);
            preparedStatement.setString(1, objects.getName());
            preparedStatement.setString(2, objects.getParentPath());
            preparedStatement.setString(3, objects.getType());
            preparedStatement.setLong(4, objects.getSize());
            int result = preparedStatement.executeUpdate();
            connection.close();

            return result == 1;

        } catch (SQLException e) {
            log.error("Send event fail {}:{}", url, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean isAlive() {
        return this.isAlive;
    }
}
